<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
		"http://www.w3.org/TR/html4/strict.dtd">
<html>
	<head>
		<title>doh.robot Spinner Test</title>

		<style>
			@import "../../../../util/doh/robot/robot.css";
		</style>

		<!-- required: dojo.js -->
		<script type="text/javascript" src="../../../../dojo/dojo.js"
			djConfig="isDebug: true, parseOnLoad: true"></script>

		<script type="text/javascript">
			dojo.require("dijit.dijit"); // optimize: load dijit layer
			dojo.require("dijit.robotx");

			dojo.addOnLoad(function(){
				var spin1;
				var spin2;
				var spin3;
				var spinnerIds = ["integerspinner1", "integerspinner2", "integertextbox3", "realspinner1"];
				doh.robot.initRobot('../test_Spinner.html');

				// focus handlers for noticing tab/shift+tab navigation
				var focusCountZ = 0;
				var blurCountZ = 0;
				var countFocus = function(){
					focusCountZ++;
				}
				var countBlur = function(){
					blurCountZ++;
				}
				var focusConnect;
				var blurConnect;
				var tabFocusSetup = function(inSpinnerId){
					var spinner = dijit.byId(inSpinnerId);
					dijit.byId('integerspinner1').focus();
					focusCountZ = 0;
					blurCountZ = 0;
					focusConnect = spinner.connect(spinner.focusNode, "onfocus", countFocus);
					blurConnect = spinner.connect(spinner.focusNode, "onblur", countBlur);
				}

				// Setup to test manipulation of spinner via keystrokes.
				var results;	// array holding both expected value and actual value after each keypress
				var strokeIndex = 0;
				var noteConnect;
				function keyStrokeSetup(inSpinnerId){
					// summary:
					//		Populates results[] with expected values after each keypress.
					//		Also sets up listener to every keyboard event (arrow key etc) on a spinner and
					//		records to record the actual spinner values into results[].
					var spinner = dijit.byId(inSpinnerId);
					results = populateExpected(spinner);
					strokeIndex = 0;
					noteConnect = spinner.connect(spinner.focusNode, "onkeypress", function(){
						results[strokeIndex].actual = spinner.attr('value');
						strokeIndex++;
					});
				}

				// Use spinner api to calculate values for a set of keystrokes.
				// Returns array of expected values.
				var populateExpected = function(/*Spinner Widget*/inSpinner){
					var initVal = inSpinner.attr('value');
					var eVals = [];
					var newVal;

					// The values expected by pressing HOME, right arrow five times,
					// PgUp twice, PgDn, up arrow twice, down arrow once,
					// left arrow once, and END.
					if(inSpinner.constraints.min){
						inSpinner.attr('value', inSpinner.constraints.min);
					}
					newVal = inSpinner.attr('value');
					eVals.push({stroke: "HOME", expected: newVal});
					// UP 5 times
					for(var i = 0; i < 5; i++){
						newVal = inSpinner.adjust(newVal, inSpinner.smallDelta);
						inSpinner.attr('value', newVal);
						newVal = inSpinner.attr('value');
						eVals.push({stroke: "UP", expected: newVal});
					}
					// PgUp twice
					for(var i = 0; i < 2; i++){
						newVal = inSpinner.adjust(newVal, inSpinner.largeDelta);
						inSpinner.attr('value', newVal);
						newVal = inSpinner.attr('value');
						eVals.push({stroke: "PgUp", expected: newVal});
					}
					// PgDn once.
					newVal = inSpinner.adjust(newVal, -inSpinner.largeDelta);
					inSpinner.attr('value', newVal);
					newVal = inSpinner.attr('value');
					eVals.push({stroke: "PgDn", expected: newVal});
					// DOWN
					newVal = inSpinner.adjust(newVal, -inSpinner.smallDelta);
					inSpinner.attr('value', newVal);
					newVal = inSpinner.attr('value');
					eVals.push({stroke: "DOWN", expected: newVal});
					// END
					if(inSpinner.constraints.max){
						inSpinner.attr('value', inSpinner.constraints.max);
						newVal = inSpinner.attr('value');
					}else{
						newVal = inSpinner.attr('value');
					}
					eVals.push({stroke: "END", expected: newVal});

					// reset <inSpinner> back to its initial value, and return
					inSpinner.attr('value', initVal);
					return eVals;
				}

				// common robot TAB-focus test function.
				var a11yTabFocus = function(inSpinnerId){
					var d = new doh.Deferred();
					var spinner = dijit.byId(inSpinnerId);
					// insure a known focus starting point within document before key presses.
					spinner.focus();
					// Shift-tab away, tab to, tab away, shift-tab back.
					doh.robot.keyPress(dojo.keys.TAB, 1000, {shift:true});
					doh.robot.keyPress(dojo.keys.TAB, 1000);
					doh.robot.keyPress(dojo.keys.TAB, 1000);
					doh.robot.keyPress(dojo.keys.TAB, 1000, {shift:true});

					// TODO: this doesn't seem thorough... after TAB or shift TAB
					// should be checking that we ended up at the right place, i.e.
					// make sure that we don't have extra tab stops within the Spinner itself.

					var checkGotFocus = function(){
						spinner.disconnect(focusConnect);
						spinner.disconnect(blurConnect);
						doh.assertEqual(3, focusCountZ, "# of times focused (" + spinner.id + ")");
						doh.assertEqual(2, blurCountZ, "# of times lost focus (" + spinner.id + ")");
					};
					doh.robot.sequence(d.getTestCallback(checkGotFocus), 1000, 1000);
					return d;
				}

				// common robot keystroke test function.
				var a11yKeystrokeTest = function(inSpinnerId){
					var d = new doh.Deferred();
					var spinner = dijit.byId(inSpinnerId);
					var initVal = spinner.attr('value');

					spinner.focus();

					doh.robot.keyPress(dojo.keys.HOME, 1000);
					doh.robot.keyPress(dojo.keys.UP_ARROW, 200);
					doh.robot.keyPress(dojo.keys.UP_ARROW, 200);
					doh.robot.keyPress(dojo.keys.UP_ARROW, 200);
					doh.robot.keyPress(dojo.keys.UP_ARROW, 200);
					doh.robot.keyPress(dojo.keys.UP_ARROW, 200);
					doh.robot.keyPress(dojo.keys.PAGE_UP, 200);
					doh.robot.keyPress(dojo.keys.PAGE_UP, 200);
					doh.robot.keyPress(dojo.keys.PAGE_DOWN, 200);
					doh.robot.keyPress(dojo.keys.DOWN_ARROW, 200);
					doh.robot.keyPress(dojo.keys.END, 200);
					var testPresses = function(){
						spinner.disconnect(noteConnect);
						spinner.attr('value', initVal);
						for(var i = 0; i < results.length; i++){
							var aResult = results[i];
							if(!(isNaN(aResult.expected) && isNaN(aResult.actual))) {
								doh.is(aResult.expected, aResult.actual, aResult.stroke);
							}
						}
					};
					doh.robot.sequence(d.getTestCallback(testPresses), 1000, 1000);
					return d;
				}

				doh.register("setUp",{
					name: "setUp",
					timeout: 15000,
					setUp:function(){
						spin1 = dijit.byId('integerspinner1');
						spin2 = dijit.byId('integerspinner2');
						spin3 = dijit.byId('realspinner1');
						spin4 = dijit.byId('spinnerMinOnly');
						safeClick = dojo.byId('form1');
					},
					runTest: function(){
						// assert onChange not fired
						doh.is("not fired yet!", dojo.byId('oc1').value);

						// make sure initial values are what we expect
						doh.is(1, spin1.smallDelta);
						doh.is(900, spin1.attr('value'), "integerspinner1");
						doh.is(1000, spin2.attr('value'), "integerspinner2");
						doh.is(1.0, spin3.attr('value'), "realspinner1");
						doh.is(1.0, spin4.attr('value'), "spinnerMinOnly");
					}
				});

				doh.register("dojo.query() input by name",
					dojo.map(spinnerIds, function(id){
						return {
							name: id,
							spinner: id,
							spinnerName: id,
							runTest:function(){
								this.spinner = dijit.byId(this.spinner);
								var queried=dojo.query("input[name="+this.spinnerName+"]");
								doh.is(1, queried.length,"Expected 1 spinner with name "+this.spinnerName+", found "+queried.length);
								doh.is(this.spinner.valueNode, queried[0],"Spinner's valueNode did not match the one found by dojo.query.");
							}
						};
					})
				);

				doh.register("a11y", [
					{
						name: "spinner2_typematic",
						timeout: 15000,
						setUp:function(){
							spin2.attr('value', 900);
							spin2.focus();
						},
						runTest: function(){
							// test typematic
							var d=new doh.Deferred();
							doh.robot.keyDown(dojo.keys.DOWN_ARROW, 1000);
							doh.robot.keyUp(dojo.keys.DOWN_ARROW, 5000);
							doh.robot.sequence(function(){
								if(spin2.attr('value')<=800){
									d.callback(true);
								}else{
									d.errback(new Error('Error in typematic test. Expected <=800, got '+spin2.attr('value')));
								}
							}, 1000);
							return d;
						}
					},

					{
						name: "spinner2_max",
						timeout: 15000,
						setUp:function(){
							spin2.attr('value', 1549);
							spin2.focus();
						},
						runTest: function(){
							// test max with arrow key
							var d=new doh.Deferred();
							// press once: should move up
							doh.robot.keyPress(dojo.keys.UP_ARROW, 1000);
							doh.robot.sequence(d.getTestErrback(function(){
								doh.is(1550, spin2.attr('value'));
								doh.is("1550", spin2.focusNode.value);
								doh.is(true, spin2.isValid());
								// press again: shouldn't move
								doh.robot.keyPress(dojo.keys.UP_ARROW, 500);
								doh.robot.sequence(d.getTestCallback(function(){
									doh.is(1550, spin2.attr('value'));
									doh.is("1550", spin2.focusNode.value);
									doh.is(true, spin2.isValid());
								}), 500);
							}), 500);

							return d;
						}
					},

					{
						name: "spinner2_min",
						timeout: 15000,
						setUp:function(){
							spin2.attr('value', 10);
							spin2.focus();
						},
						runTest: function(){
							// test min with arrow key
							var d=new doh.Deferred();
							// press once: should move up
							doh.robot.keyPress(dojo.keys.DOWN_ARROW, 1000);
							doh.robot.sequence(d.getTestErrback(function(){
								doh.is(9, spin2.attr('value'));
								doh.is("9", spin2.focusNode.value);
								doh.is(true, spin2.isValid());
								// press again: shouldn't move
								doh.robot.keyPress(dojo.keys.DOWN_ARROW, 500);
								doh.robot.sequence(d.getTestCallback(function(){
									doh.is(9, spin2.attr('value'));
									doh.is("9", spin2.focusNode.value);
									doh.is(true, spin2.isValid());
								}), 500);
							}), 500);

							return d;
						}
					},

					{
						name: "spinner2_invalid",
						timeout: 15000,
						setUp:function(){
							spin2.focusNode.value="";
							spin2.focus();
						},
						runTest: function(){
							// assert invalid works
							var d=new doh.Deferred();
							doh.robot.typeKeys("0.5", 1000, 600);
							doh.robot.sequence(d.getTestCallback(function(){
								doh.is(false, spin2.isValid());
							}), 500);
							return d;
						}
					}
				]);

				// Test ARIA role.  Since role attribute is set in template,
				// checking one should be sufficient.
				doh.register("a11yAriaRole",
					function spinnerRole(){
						var spinner = dijit.byId("integerspinner1");
						doh.isNot(spinner, null, "can't find 'integerspinner1'");
						doh.is(dijit.getWaiRole(spinner.focusNode), "spinbutton", spinner.id + ": aria role (spinbutton)");
					}
				);

				// Check ARIA min/max values.
				// - both min and max.
				// - min, no max.
				// - max, no min.
				// - neither.
				doh.register("a11yMinMaxValues",
					[
						function minAndMax(){
							var spinner = dijit.byId("integerspinner2");
							doh.isNot(spinner, null, "can't find 'integerspinner2'");
							doh.is(dijit.getWaiState(spinner.focusNode, "valuemin"), spinner.constraints.min, spinner.id + ": aria-valuemin");
							doh.is(dijit.getWaiState(spinner.focusNode, "valuemax"), spinner.constraints.max, spinner.id + ": aria-valuemax");
						},
						function minOnly(){
							var spinner = dijit.byId("spinnerMinOnly");
							doh.isNot(spinner, null, "can't find 'spinnerMinOnly'");
							doh.is(dijit.getWaiState(spinner.focusNode, "valuemin"), spinner.constraints.min, spinner.id + ": aria-valuemin");
							if(spinner.constraints.max){
								doh.is(dijit.getWaiState(spinner.focusNode, "valuemax"), spinner.constraints.max, spinner.id + ": aria-valuemax");
							} else {
								doh.f(dijit.hasWaiState(spinner.focusNode, "valuemax"), spinner.id + ": aria-valuemax");
							}
						},
						function maxOnly(){
							var spinner = dijit.byId("integertextbox3");
							doh.isNot(spinner, null, "can't find 'integertextbox3'");
							doh.is(dijit.getWaiState(spinner.focusNode, "valuemax"), spinner.constraints.max, spinner.id + ": aria-valuemax");
							if(spinner.constraints.min){
								doh.is(dijit.getWaiState(spinner.focusNode, "valuemin"), spinner.constraints.min, spinner.id + ": aria-valuemin");
							} else {
								doh.f(dijit.hasWaiState(spinner.focusNode, "valuemin"), spinner.id + ": aria-valuemin");
							}
						},
						function neitherMinNorMax(){
							var spinner = dijit.byId("integertextbox3");
							doh.isNot(spinner, null, "can't find 'integertextbox3'");
							if(spinner.constraints.min){
								doh.is(dijit.getWaiState(spinner.focusNode, "valuemin"), spinner.constraints.min, spinner.id + ": aria-valuemin");
							} else {
								doh.f(dijit.hasWaiState(spinner.focusNode, "valuemin"), spinner.id + ": aria-valuemin");
							}
							if(spinner.constraints.max){
								doh.is(dijit.getWaiState(spinner.focusNode, "valuemax"), spinner.constraints.max, spinner.id + ": aria-valuemax");
							} else {
								doh.f(dijit.hasWaiState(spinner.focusNode, "valuemax"), spinner.id + ": aria-valuemax");
							}
						}
					]
				);

				// Loop to test each spinner for aria-valuenow
				// TODO:
				//		Is testing one spinner sufficient?
				//
				// test aria valuemin, valuemax, valuenow.
				doh.register("a11yValueNow",
					dojo.map(spinnerIds, function(id){
						return {
							name: id+"_value",
							spinnerId: id,
							runTest:function(){
								var spinner = dijit.byId(this.spinnerId);
								spinner.attr('value', 100);
								var ariaVal = dijit.getWaiState(spinner.focusNode, "valuenow");
								doh.is(100, ariaVal, spinner.id + ": aria-valuenow");
							}
						};
					})
				);


					// test "null" aria-valuenow
/* Remove null aria-valuenow test until #7866 is resolved
					doh.register("a11yNullValuenow", {
						name:spinnerIds[i]+"_nullValueNow",
						spinnerId:spinnerIds[i],
						runTest:function(){
							var spinner = dijit.byId(this.spinnerId);
							var initVal = spinner.attr('value');
							spinner.attr('value', null);
							var spinnerVal = spinner.attr('value');
							var nowVal = dijit.getWaiState(spinner.focusNode, "valuenow");
							var invalid = dijit.getWaiState(spinner.focusNode, "invalid");
							spinner.attr('value', initVal);
							doh.t(invalid, spinner.id + ": aria-invalid");
							if(!(isNaN(spinnerVal) && isNaN(nowVal))){
								doh.is(spinnerVal, nowVal, spinner.id + ": null aria-valuenow");
							}
						}
					});
*/

				// a11y tab focus tests (robot)
				doh.register("a11yTabFocus",
				[
					// don't test integerspinner1 since shift-tab from there goes to address bar and the robot doesn't like that at all
					{
						name:"integertextbox3TabFocus",
						timeout:9000,
						setUp:function(){
							tabFocusSetup("integertextbox3");
						},
						runTest:function(){
							return a11yTabFocus("integertextbox3");
						}
					},
					{ // test integerspinner2 second since the previous test group already had it focused
						name:"integerspinner2TabFocus",
						timeout:9000,
						setUp:function(){
							tabFocusSetup("integerspinner2");
						},
						runTest:function(){
							return a11yTabFocus("integerspinner2");
						}
					},
					{
						name:"realspinner1TabFocus",
						timeout:9000,
						setUp:function(){
							tabFocusSetup("realspinner1");
						},
						runTest:function(){
							return a11yTabFocus("realspinner1");
						}
					}
				]);

				// a11y keystroke tests
				doh.register("a11yKeystrokes",
				[
					{
						name:"integerspinner1Keystrokes",
						timeout:9000,
						setUp:function(){
							keyStrokeSetup("integerspinner1");
						},
						runTest:function(){
							return a11yKeystrokeTest("integerspinner1");
						}
					},
					{
						name:"integerspinner2Keystrokes",
						timeout:9000,
						setUp:function(){
							keyStrokeSetup("integerspinner2");
						},
						runTest:function(){
							return a11yKeystrokeTest("integerspinner2");
						}
					},
					{
						name:"integertextbox3Keystrokes",
						timeout:9000,
						setUp:function(){
							keyStrokeSetup("integertextbox3");
						},
						runTest:function(){
							return a11yKeystrokeTest("integertextbox3");
						}
					},
					{
						name:"realspinner1Keystrokes",
						timeout:9000,
						setUp:function(){
							keyStrokeSetup("realspinner1");
						},
						runTest:function(){
							return a11yKeystrokeTest("realspinner1");
						}
					}
				]);

				// exponential value keystroke tests
				doh.register("exponential",
				[
					{
						name: "spinnerMinOnly large exp",
						timeout: 15000,
						setUp:function(){
							spin4.focusNode.value="";
							spin4.focus();
						},
						runTest: function(){
							var d=new doh.Deferred();
							doh.robot.typeKeys("0.5e99", 1000, 1200);
							doh.robot.keyPress(dojo.keys.TAB, 500);
							doh.robot.sequence(d.getTestCallback(function(){
								doh.f(spin4.isValid(false), "large exponential is invalid");
								doh.f(spin4.isInRange(false), "large exponential is out of range");
								doh.is(spin4.getErrorMessage(true), spin4.rangeMessage, "large exponential showing out of range message");
								doh.is("5e+98", spin4.attr('displayedValue'), "large exponential reformatted to standard form");
							}), 1000);
							return d;
						}
					},
					{
						name: "spinnerMinOnly adjust small exp",
						timeout: 15000,
						setUp:function(){
							spin4.focusNode.value="";
							spin4.focus();
						},
						runTest: function(){
							var d=new doh.Deferred();
							doh.robot.typeKeys("0.5e9", 1000, 1000);
							doh.robot.keyPress(dojo.keys.UP_ARROW, 500);
							doh.robot.keyPress(dojo.keys.TAB, 500);
							doh.robot.sequence(d.getTestCallback(function(){
								doh.t(spin4.isValid(false), "small exponential is valid");
								doh.t(spin4.isInRange(false), "small exponential is in range");
								doh.is(500000000.1, spin4.attr('value'), "small exponential converted to whole number");
								doh.is("500,000,000.1", spin4.attr('displayedValue'), "small exponential converted to whole number with formatting");
							}), 1000);
							return d;
						}
					},
					{
						name: "spinnerMinOnly adjust max exp",
						timeout: 15000,
						setUp:function(){
							spin4.focusNode.value="";
							spin4.focus();
						},
						runTest: function(){
							var d=new doh.Deferred();
							doh.robot.typeKeys("0.5e99", 1000, 1200);
							doh.robot.keyPress(dojo.keys.DOWN_ARROW, 500); // convert to max
							doh.robot.keyPress(dojo.keys.DOWN_ARROW, 500);
							doh.robot.keyPress(dojo.keys.TAB, 500);
							doh.robot.sequence(d.getTestCallback(function(){
								doh.t(spin4.isValid(false), "large exponential converted to max is valid");
								doh.t(spin4.isInRange(false), "max value is in range");
								doh.is(89999999999999.9, spin4.attr('value'), "max value (-.1)");
								doh.is("89,999,999,999,999.9", spin4.attr('displayedValue'), "max value (-.1) with formatting");
							}), 1000);
							return d;
						}
					}
				]);

				// invalid editing keystroke tests
				doh.register("invalid editing",
				[
					{
						name: "HOME key, out of range",
						timeout: 15000,
						setUp:function(){
							spin4.focusNode.value="";
							spin4.focus();
						},
						runTest: function(){
							var d=new doh.Deferred();
							doh.robot.typeKeys("0.5e99", 1000, 1200);
							doh.robot.keyPress(dojo.keys.HOME, 500);
							doh.robot.sequence(d.getTestCallback(function(){
								doh.t(spin4.isValid(true), "HOME changed large exponential to valid min, value is instead: "+spin4.focusNode.value);
								doh.t(spin4.isInRange(true), "HOME changed large exponential to in range");
								doh.is(-10.9, spin4.attr('value'), "HOME changed large exponential to min value");
								doh.is("-10.9", spin4.attr('displayedValue'), "HOME changed large exponential to min display");
							}), 500);
							return d;
						}
					},
					{
						name: "HOME key, invalid",
						timeout: 15000,
						setUp:function(){
							spin4.focusNode.value="";
							spin4.focus();
						},
						runTest: function(){
							var d=new doh.Deferred();
							doh.robot.typeKeys(".5e9a", 1000, 1000);
							doh.robot.keyPress(dojo.keys.HOME, 500);
							if(dojo.isMac){
								doh.robot.keyPress(dojo.keys.LEFT_ARROW, 500, {meta:true});
							}
							doh.robot.typeKeys("1", 500, 200);
							doh.robot.keyPress(dojo.keys.END, 500);
							if(dojo.isMac){
								doh.robot.keyPress(dojo.keys.RIGHT_ARROW, 500, {meta:true});
							}
							doh.robot.keyPress(dojo.keys.BACKSPACE, 500);
							doh.robot.sequence(d.getTestCallback(function(){
								doh.t(spin4.isValid(true), "edited exponential is now valid");
								doh.is(1.5e+9, spin4.attr('value'), "edited exponential");
							}), 500);
							return d;
						}
					}
				]);

				doh.register("rounding",
				[
					{
						name: "simple",
						timeout: 15000,
						runTest: function(){
							var d=new doh.Deferred();
							var spinner = dijit.byId("integertextbox3");
							spinner.attr('value', '');
							spinner.focus();
							doh.robot.typeKeys("1.2339", 1000, 1200);
							doh.robot.keyPress(dojo.keys.TAB, 500);
							doh.robot.sequence(d.getTestCallback(function(){
								doh.t(spinner.isValid(false), "no error when rounding");
								doh.is(1.234, spinner.attr('value'), "value is rounded");
								doh.is("1.234", spinner.attr('displayedValue'), "rounded value correctly displayed");
							}), 1000);
							return d;
						}
					}
				]);

				doh.run();
			});
		</script>
	</head>
</html>
